const socket = require('socket.io');
const { v4: uuidv4 } = require('uuid');
const Peer = require("./Peer.js");
const mediasoup = require("mediasoup");

// room就是房间，房间里面就是router，router和room等价。
// router 存在于一个 Worker 中，代表一个视频会议房间。 所有的媒体流都在这个 Router 内部进行路由和转发。

class Room {


  constructor(room_id, worker, io, router) {
    this.room_id = room_id;
    this.io = io;

    
    // 创建router
    this.router = router;
    // 创建
    this.peers = new Map();
  }

  // 静态异步工厂方法创建 Room
  // 这样做的好处是确保router可用！因为worker.createRouter是个异步的，必须等待它返回才可以确保创建出的Room中的router可用！
  static async create(room_id, worker, io) {
    const mediaCodecs =
    [
        {
            kind        : "audio",
            mimeType    : "audio/opus",
            clockRate   : 48000,
            channels    : 2
        },
        {
            kind       : "video",
            mimeType   : "video/H264",
            clockRate  : 90000,
            parameters :
            {
            "packetization-mode"      : 1,
            "profile-level-id"        : "42e01f",
            "level-asymmetry-allowed" : 1
            }
        }
    ];
    const router = await worker.createRouter({ mediaCodecs });
    return new Room(room_id, worker, io, router);
  }

  // WebRtcTransport 代表一个客户端与 MediaSoup 服务器之间的媒体连接通道。
  async createWebRtcTransport(socketId) {
    
    console.log(`room进入 createWebRtcTransport`);
    const transport = await this.router.createWebRtcTransport({
        /* 
            在创建 WebRtcTransport 时，通过 listenIps 参数指定服务器监听的 IP 地址。这里的 announcedIp 至关重要，它应该设置为你的 MediaSoup 服务器的公网 IP 地址。这本质上是在扮演一个“静态 STUN 信息”的角色。
        */
        listenIps: [
          {
             ip: '0.0.0.0', 
             announcedIp: '10.2.20.15'   // 这里相当于STUN设置，一般STUN就在信令服务器上。这里也就是填写信令服务器的ip
          }
        ], 
        enableUdp: true,
        enableTcp: true,
        preferUdp: true,
        initialAvailableOutgoingBitrate: 600000 //初始可用的输出比特率(以bps为单位)。
    });



    console.log("---adding transport---", transport.id);

    // 拿到房间里的人
    let peer = this.peers.get(socketId);
    // 给这个人连接上Transport
    peer.addTransport(transport);

    console.log("---now transport is---"+JSON.stringify(transport));
    // transport里面有一些不可枚举属性，用JSON.stringify是打印不出来的。需要额外打印其全部参数：
    // 正确的访问方式：
    console.log('Transport ID:', transport.id);
    console.log('ICE Parameters:', transport.iceParameters);
    console.log('ICE Candidates:', transport.iceCandidates);

    // 至关重要，这里是 dtlsParameters第一次出现的地方。我们可以发现是router创建出来的WebRtcTransport的一个属性。
    // 后面还要继续用这个  dtlsParameters
    console.log('DTLS Parameters:', transport.dtlsParameters);

    return {
        id: transport.id,
        iceParameters: transport.iceParameters,
        iceCandidates: transport.iceCandidates,
        dtlsParameters: transport.dtlsParameters
    }
  }

  addPeer(peer) {
    this.peers.set(peer.id, peer)
  }

  getProducerListForPeer() {
    let producerList = [];
    // 遍历房间内的每个人（其实就是每个socket）
    this.peers.forEach((peer) => {
      // 每个人（可能）有多个 producer，把这些个producer的id塞进去
      peer.producers.forEach((producer) => {
        producerList.push({
          producer_id: producer.id,
          producer_name: producer.clientName
        })
      })
    })
    return producerList;
  }

  getRtpCapabilities() {
    return this.router.rtpCapabilities
  }

  async connectPeerTransport(socket_id, transport_id, dtlsParameters) {
    if (!this.peers.has(socket_id)) return

    // 这个人（也就是这个socket ，去连接Transport）
    await this.peers.get(socket_id).connectTransport(transport_id, dtlsParameters)
  }

  async produce(socket_id, producerTransportId, rtpParameters, kind, name) {
    console.log(`服务端开始生产 ，此时 rtpParameters参数是：${rtpParameters} ，，，，， kind参数是：${kind},发起者名字为：${name}`);
    // handle undefined errors
    return new Promise(
      async function (resolve, reject) {
        // 这里是创建producer
        let producer = await this.peers.get(socket_id).createProducer(producerTransportId, rtpParameters, kind, name)
        resolve(producer.id)
        // 广播到房间内所有人
        this.broadCast(socket_id, 'newProducers', [
          {
            producer_id: producer.id,
            producer_socket_id: socket_id,
            producer_name: name
          }
        ])
      }.bind(this)
    )
  }

  async consume(socket_id, consumer_transport_id, producer_id, rtpCapabilities) {
    // handle nulls
    if (
      !this.router.canConsume({
        producerId: producer_id,
        rtpCapabilities
      })
    ) {
      console.error('can not consume')
      return
    }

    let { consumer, params } = await this.peers
      .get(socket_id)
      .createConsumer(consumer_transport_id, producer_id, rtpCapabilities)

    consumer.on(
      'producerclose',
      function () {
        console.log('Consumer closed due to producerclose event', {
          name: `${this.peers.get(socket_id).name}`,
          consumer_id: `${consumer.id}`
        })
        this.peers.get(socket_id).removeConsumer(consumer.id)
        // tell client consumer is dead
        this.io.to(socket_id).emit('consumerClosed', {
          consumer_id: consumer.id
        })
      }.bind(this)
    )

    return params
  }

  async removePeer(socket_id) {
    this.peers.get(socket_id).close()
    this.peers.delete(socket_id)
  }

  closeProducer(socket_id, producer_id) {
    this.peers.get(socket_id).closeProducer(producer_id)
  }

  // 广播，除了自己，都能收到这个消息。
  broadCast(socket_id, name, data) {
    for (let otherID of Array.from(this.peers.keys()).filter((id) => id !== socket_id)) {
      this.send(otherID, name, data)
    }
  }

  send(socket_id, name, data) {
    this.io.to(socket_id).emit(name, data)
  }

  getPeers() {
    return this.peers;
  }

  toJson() {
    return {
      id: this.id,
      peers: JSON.stringify([...this.peers])
    }
  }
}

module.exports = Room;
